package org.longteng.security.jwtkeys;

import lombok.extern.slf4j.Slf4j;

import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;


/**
 * 生成密钥对实体类
 * <br>基于ECC的SM2证书普遍采用256位密钥长度，加密强度等同于3072位RSA证书，远高于业界普遍采用的2048位RSA证书。
 * <br>ECDSA算法是ECC+DSA的结合体，相较于RSA数字签名算法，速度快，强度高，签名短，前景乐观.
 */
@Slf4j
public abstract class JwtKeyPair {

    String keyId;   // 标识
    int keySize;    // 大小，RSA:3072才安全
    PrivateKey privateKey;  // 私钥
    PublicKey publicKey;  // 公钥

    public PrivateKey getPrivateKey() {
        if (privateKey == null ) {
            log.warn("构建后要调用 initBuildKey().");
        }
        return privateKey;
    }
    public void setPrivateKey(PrivateKey privateKey) {
        this.privateKey =  privateKey;
    }

    public PublicKey getPublicKey() {
        if(publicKey == null) {
            log.warn("构建后要调用 initBuildKey().");
        }
        return publicKey;
    }
    public void setPublicKey(PublicKey publicKey) {
        this.publicKey =  publicKey;
    }

    public String getKeyId() {
        return keyId;
    }

    public int getKeySize() {
        return keySize;
    }

    /**
     * 返回公钥字符串
     * @return
     */
    public String getPublicKeyEncBASEStr() {
        if(publicKey == null) {
            log.warn("构建后要调用 initBuildKey().");
            return null;
        }
        return Base64.getEncoder().encodeToString(publicKey.getEncoded());
    }

    /**
     * 返回公钥字符串
     * @return
     */
    public String getPrivateKeyEncBASEStr() {
        if (privateKey == null ) {
            log.warn("构建后要调用 initBuildKey().");
            return null;
        }
        return Base64.getEncoder().encodeToString(privateKey.getEncoded());
    }

    /**
     * 公钥字符串转换成公钥规格
     * @param pubKeyBaseStr
     * @return X509EncodedKeySpec
     */
    public KeySpec readPubKeySpec(String pubKeyBaseStr) {
        // 把 公钥的Base64文本 解码为已编码的 公钥bytes
        byte[] encPubKey = Base64.getDecoder().decode(pubKeyBaseStr);

        // 创建 已编码的公钥规格
        X509EncodedKeySpec encPubKeySpec = new X509EncodedKeySpec(encPubKey);
        return encPubKeySpec;
    }

    /**
     * 私钥字符串转换成私钥规格
     * @param priKeyBaseStr
     * @return PKCS8EncodedKeySpec
     */
    public KeySpec readPriKeySpec(String priKeyBaseStr) {
        // 把 私钥的Base64文本 解码为已编码的 私钥bytes
        byte[] encPriKey = Base64.getDecoder().decode(priKeyBaseStr);

        // 创建 已编码的私钥规格
        PKCS8EncodedKeySpec encPriKeySpec = new PKCS8EncodedKeySpec(encPriKey);
        return encPriKeySpec;
    }

    /**
     * 返回实现的加密类型
     * @return
     */
    public abstract String getAlgorithmName();

    /**
     * 返回实现的加签名类型
     * @return
     */
    public abstract String getAlgSignatureName();

    /**
     * 公钥字符串转换成公钥
     * @param pubKeyBaseStr
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public abstract PublicKey reaPubKey(String pubKeyBaseStr) throws NoSuchAlgorithmException, InvalidKeySpecException;

    /**
     * 私钥字符串转换成私钥
     * @param priKeyBaseStr
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public abstract PrivateKey reaPriKey(String priKeyBaseStr) throws NoSuchAlgorithmException, InvalidKeySpecException;
    /**
     * 生成具体的密钥对
     * @return JwtKeyPair
     */
    public abstract JwtKeyPair initBuildKey() throws NoSuchAlgorithmException;
}
